//
// Created by liaohailong on 2025/4/18.
//

#include "HelloBitStreamHandler.hpp"

HelloBitStreamHandler::HelloBitStreamHandler(const std::shared_ptr<HelloCacheManager> &cache) :
        HelloRequestHandler("HelloBitStreamHandlerVictor"), cache(cache)
{
    logger.i("HelloBitStreamHandler::HelloBitStreamHandler(%p)", this);
}

HelloBitStreamHandler::~HelloBitStreamHandler()
{
    logger.i("HelloBitStreamHandler::~HelloBitStreamHandler(%p)", this);
}


void HelloBitStreamHandler::acceptClient(std::shared_ptr<HelloHttpRequest> request)
{
    // 客户端连接的 socket fd 用来回写数据
    const int connFd = request->getConnFd();
    logger.i("onClientRequest client fd[%d] start", connFd);

    // 取出 query参数,里面必须有url
    const std::string &url = request->getRealUrl();
    if (url.empty())
    {
        logger.e("no 'url' query param in request?");
        return;
    }
    std::shared_ptr<HelloRange> range = request->getRange(); // origin range
    std::vector<std::shared_ptr<HelloDataFetcher>> combinedFetchers; // [local + network]

    // 客户端请求，会出现情况：
    // 全量请求 or 部分请求
    // 有缓存 or 无缓存

    // 取出本地缓存信息,区分 有缓存 和 没缓存
    const std::shared_ptr<HelloCacheInfo> &cacheInfo = cache->getCacheInfo(url);
    if (cacheInfo->available()) // 有缓存
    {
        const std::string &cacheDir = cacheInfo->cacheDir->getAbsolutFilepath();
        logger.i("client[%d] found cache dir: [%s]", connFd, cacheDir.c_str());
        // 一个临时副本，方便计算缓存范围
        std::shared_ptr<HelloRange> tempRange = std::make_shared<HelloRange>(range->start,
                                                                             range->end);
        // 是否为请求部分资源
        bool partial = tempRange->available();

        int64_t length = cacheInfo->getContentLength(); // 本地缓存，按理说不可能没有content-length，这里返回的内容是有效值
        // 更新请求数据范围
        if (partial)
        {
            // Range: bytes 0-\r\n 这种情况下，我们补齐这个end，就是文件长度-1
            if (tempRange->end < 0)
            {
                tempRange->end = length - 1;
            }
        } else
        {
            // 这里是请求全量，客户端请求中没写Range字段，我们也补齐具体范围
            tempRange->start = 0;
            tempRange->end = length - 1;
        }

        /*
         * 命中缓存的情况：
         * 客户端请求范围的start落在我们具体一个缓存区间之中：
         * case1    命中部分缓存N字节
         * client:      [0,200]
         * cached:      [0,100]
         *               local   net
         * splits:      [0,100] [101,200]
         *
         * case2    命中部分缓存1字节
         * client:      [50,200]
         * cached:      [49,50]
         *
         *               local   net
         * splits:      [50,50] [51,200]
         *
         * case3    全部命中缓存
         * client:      [50,200]
         * cached:      [0,500]
         *
         *               local
         * splits:      [50,200]
         *
         * 其余的暂不处理，每次下载分片后，合并区间
         * */
        std::shared_ptr<HelloRange> cachedRange = cache->getCachedRange(cacheInfo, tempRange);
        logger.i("client[%d] try to read cache, tempRange[%d,%d] -> cachedRange[%d,%d]",
                 connFd, tempRange->start, tempRange->end, cachedRange->start, cachedRange->end);
        // 本地存在缓存,写回客户端
        if (cachedRange->available())
        {
            // 直接回写
            auto fetcher = std::make_shared<HelloLocalFetcher>(cache, request, tempRange);
            fetcher->setCachedRange(cachedRange);
            combinedFetchers.emplace_back(fetcher);

            logger.i("client[%d] tempRange[%d,%d] and cachedRange[%d,%d]", connFd,
                     tempRange->start, tempRange->end,
                     cachedRange->start, cachedRange->end);
            // 计算(裁剪)出还剩多少范围，需要继续请求网络
            tempRange = tempRange->remove(cachedRange);
            logger.i("client[%d] remaining tempRange[%d,%d]", connFd,
                     tempRange->start, tempRange->end);

            if (tempRange->getLength() > 0)
            {
                // 把剩余的范围赋值给 客户端原始请求范围 后续会请求网络资源
                range = tempRange;
            } else
            {
                // 本地缓存涵盖了本次客户端的请求，无需再请求网络资源
                range = nullptr;
            }
        }
    }

    // 接下来判断：还需要请求网络资源数据
    if (range)
    {
        // 特殊情况,如果当前url资源正在下载网络(记录中存在),本次的网络请求就不在缓存到本地,直接写回客户端
        bool write2Cache = !hasUrl(url); // 两个线程同时往一个文件写会挂
        std::string w(write2Cache ? "true" : "false");
        logger.i("client[%d] write2Cache[%s]", connFd, w.c_str());
        request->setWrite2Cache(write2Cache); // 标记一下，是否需要同时写入本地缓存
        // 需要请求网络资源
        if (write2Cache && !cacheInfo->ensure())
        {
            // 一般不会出现这种情况
            const std::string &cacheDir = cacheInfo->cacheDir->getAbsolutFilepath();
            logger.e("cannot ensure cache dir [%s]", cacheDir.c_str());
            return;
        }
        auto fetcher = std::make_shared<HelloNetworkFetcher>(cache, request, range);
        combinedFetchers.emplace_back(fetcher);
        addUrl(url);
    }

    if (combinedFetchers.empty())
    {
        // 没有请求任务?不可能
        logger.e("combinedFetchers is empty?");
        return;
    }

    // 任务串起来,链表结构
    std::shared_ptr<HelloDataFetcher> head = combinedFetchers[0];
    std::shared_ptr<HelloDataFetcher> prev = head;
    for (int i = 1; i < combinedFetchers.size(); ++i)
    {
        std::shared_ptr<HelloDataFetcher> curr = combinedFetchers[i];
        prev->setNext(curr);
        prev = curr;
    }

    // 链头开始执行
    head->setOnCompleteCallback(HelloBitStreamHandler::OnCompleteCallback, this); // 所有任务结束回调
    addDataFetcher(connFd, head); // 记录任务,方便取消
    head->start();
}


void HelloBitStreamHandler::OnCompleteCallback(HelloDataFetcher *fetcher, void *userdata)
{
    auto native = reinterpret_cast<HelloBitStreamHandler *>(userdata);
    Logger &logger = native->logger;

    // 任务执行完毕,消除记录
    const std::shared_ptr<HelloHttpRequest> &request = fetcher->getRequest();
    const int fd = request->getConnFd();
    const std::string &url = request->getRealUrl();

    // 如果有写入本地缓存,就合并分片文件
    if (request->isWrite2Cache())
    {
        const std::shared_ptr<HelloCacheInfo> &cacheInfo = fetcher->getCacheInfo();
        bool success = cacheInfo->combineSegments();
        std::string ret(success ? "true" : "false");
        logger.i("client[%d] combineSegments complete[%s]", fd, ret.c_str());
    }

    native->removeUrl(url);
    logger.i("client[%d] ALL MISSIONS COMPLETE!", fd);
}